/*
	Filename	:	hikeguts.cpp
	Author(s)	:	Ray E. Bornert II
	Date		:	2003-JAN-14
	Copyright	:	(c) 2003 RealTimeGaming
	History		:
*/


//INCLUDE
#include <stdlib.h>
#include <assert.h>
#include "lib.h"
#include "slotguts.h"
#include "hikeguts.h"
#include "slotspin.h"
#include "getrand.h"

//STATIC VALUES FOR 6-STOP HIKE
const int HikeSlotGuts::m_maximumDieface=6;		//number of sides on the die
const int HikeSlotGuts::m_maximumPayline=16;	//number of dierolls possible
const int HikeSlotGuts::m_maximumHistory=720;	//total number of collection states
const int HikeSlotGuts::m_maximumCredits=10;	//most credits allowed per game
const int HikeSlotGuts::m_theGutsID=0x50*100;	//the official guts id for the db

/*///////////////////////////////////////////////////////////////
	HIKESLOTGUTS::PAYTABLE6STOP

	This is the standard paytable for 6-STOP hike
	It is expressed in milli-credits
	The extra digit of precision is there to reward
	players that play more than 1 credit

	for a 1 credit game ending on the (1,1) event
		the player would be paid 7 centi-credits
	for a 10 credit game ending on the (1,1) event
		the player would be paid 74 centi-credits

	A payout should be calculated as follows:

	1) find the payout amount for the given event (x,y)
	2) multiply the amount by the number of credits wagered
	3) round down to the nearest centi-credit

	example:
		7 credits wagered on a game that ends on the (11,3) event
	1) (11,3) payout amount is 5617
	2) 5617 * 7 = 39319 (millicredits)
	3) 39319 / 10 = 3931 (centicredits)		
////////////////////////////////////////////////////////////////*/

const int HikeSlotGuts::m_PayTable6Stop[18][8] =
{
// table values are in milli-credits
//    -       1       2       3       4       5       6  -
//================================================================
	{ 0,      0,      0,      0,      0,      0,      0, 0 }, //--
	{ 0,     74,      0,      0,      0,      0,      0, 0 }, // 1
	{ 0,     86,    444,      0,      0,      0,      0, 0 }, // 2
	{ 0,    111,    333,   2666,      0,      0,      0, 0 }, // 3
	{ 0,    135,    333,   1333,  16000,      0,      0, 0 }, // 4
	{ 0,    185,    370,   1061,   6000,  96000,      0, 0 }, // 5
	{ 0,    271,    469,   1061,   3839,  28790, 576000, 0 }, // 6
	{ 0,    395,    629,   1222,   3259,  15358, 144000, 0 }, // 7
	{ 0,    604,    913,   1555,   3358,  11172,  65827, 0 }, // 8
	{ 0,   1000,   1407,   2185,   4024,  10148,  41913, 0 }, // 9
	{ 0,   1740,   2320,   3345,   5456,  11049,  33851, 0 }, //10
	{ 0,   3259,   4160,   5617,   8271,  14135,  33209, 0 }, //11
	{ 0,   6703,   8209,  10469,  14160,  21185,  39037, 0 }, //12
	{ 0,  15604,  18345,  22148,  27827,  37160,  55395, 0 }, //13
	{ 0,  42950,  48370,  55345,  64691,  77814,  97617, 0 }, //14
	{ 0, 153407, 164358, 177012, 191753, 209185, 230111, 0 }, //15
	{ 0, 920456, 920456, 920456, 920456, 920456, 920456, 0 }, //16
	{ 0,      0,      0,      0,      0,      0,      0, 0 }, //--
//================================================================
};


/*///////////////////////////////////////////////////////////////////////////////
	HIKESLOTGUTS::EXPECTEDPAYOUT6STOP

	The present value table contains the expected value
	for each of the possible 720 unfinished games game histories
	in the 6 symbol game.
	
	Each value represented is expressed in milli-credits.
	So the 0th value of 996 actually means 0.996 credits and
	the 719th value of 920455 actually means 920.455 credits.
	Game histories for the 6-symbol game are an ordered list
	of 6 counts indicating the number
	of times each symbol has resulted.
	
	There are 720 unique game histories each mapped to the range 0 to 719.
	The first game history represented by index 0 is: { 0,0,0,0,0,0 }
	The next game history represented by index 1 is : { 0,0,0,0,0,1 }
	The last game history represented by index 719 is : { 0,1,2,3,4,5 }
	This last unfinished game history means that
		zero 1's, one 2, two 3's, three 4's, four 5's and five 6's have occurred.
	The order that they each occurred is not important
	only the counts for the purposes of determing present value.

	Note that each value of the 6 values in the 16th line
	of the master paytable is 920456 millicredits.
	Note that the expected value for the last game history according
	to the present value table is 920455 millicredits.
	
	The difference of 1 millicredit is due to rounding precision
	in the present value calculation.  This should not be an issue due
	to the fact the full present value of the game will never be
	offered to the player as a buyout.
	
	The buyout value offered to the player will always be some lesser
	percentage of the actual amount - typically 95%.
	
	So if the present expected value for a game state were 100,000
	millicredits, the buyout amount offered to the player would
	be 95,000 millicredits.
///////////////////////////////////////////////////////////////////*/
const int HikeSlotGuts::m_ExpectedPayout6Stop[720] =
{
	//table values are in milli-credits
    996,   1619,   3231,   8268,  28380, 121090,   1457,   2194,   3725,   6762,  14241,  39641,
   2590,   3182,   5017,   8196,  13513,  25375,   6272,   4885,   6463,   9852,  14940,  22576,
  21931,   9645,   8354,  10231,  14295,  20497,   1249,   1874,   3213,   6036,  13437,  39105,
   1711,   2768,   4716,   7938,  13298,  25246,   2575,   4041,   6897,  11463,  17433,  24680,
   4288,   5549,   8938,  14869,  22621,  28903,   9300,   7818,   9736,  14709,  22509,  30051,
   1892,   2192,   3390,   5740,  10524,  23083,   2055,   3140,   5293,   8880,  14100,  22015,
   2823,   4623,   8182,  14158,  21943,  28408,   4134,   6333,  11169,  19978,  32189,  40487,
   6852,   7940,  12044,  20770,  35014,  48180,   4378,   2847,   3244,   4728,   7916,  15812,
   2781,   3133,   4582,   7386,  11861,  18625,   2950,   4199,   7019,  12274,  20141,  28037,
   3830,   5779,  10170,  18897,  33000,  46268,   5726,   7530,  12231,  22564,  41956,  65754,
    974,   1450,   2520,   4986,  12095,  37925,   1339,   2118,   3600,   6216,  11147,  23521,
   2075,   3131,   5264,   8830,  14026,  21940,   3721,   4523,   7035,  11623,  18130,  25046,
   8867,   7087,   8378,  12267,  18835,  26537,   1184,   1842,   3125,   5497,  10299,  22924,
   1708,   2874,   5041,   8614,  13828,  21812,   2568,   4380,   7915,  13847,  21602,  28137,
   3926,   6108,  10898,  19636,  31787,  40144,   6715,   7781,  11841,  20499,  34670,  47858,
   1557,   2222,   3633,   6184,  10590,  19171,   2097,   3468,   6156,  10783,  17325,  24461,
   3077,   5432,  10256,  18958,  31078,  39584,   4507,   7792,  15061,  29410,  51578,  67548,
   6614,   9498,  16823,  32977,  62406,  92807,   2483,   2582,   3548,   5597,   9332,  16367,
   2517,   3468,   5661,   9833,  16467,  24523,   3228,   5202,   9534,  18131,  32097,  45464,
   4533,   7715,  15040,  30964,  60012,  90361,   6527,  10156,  19050,  40322,  86028, 151378,
   1155,   1194,   1749,   3067,   6572,  18448,   1148,   1595,   2555,   4335,   7684,  15593,
   1556,   2321,   3885,   6682,  11107,  17934,   2511,   3427,   5588,   9716,  16271,  24227,
   5250,   5400,   7311,  11723,  19731,  30642,   1078,   1459,   2303,   3922,   7155,  15183,
   1397,   2192,   3761,   6554,  10980,  17845,   2024,   3393,   6187,  11322,  19037,  26994,
   3081,   5017,   9290,  17793,  31614,  44852,   5162,   6902,  11451,  21521,  40540,  64152,
   1305,   1830,   2928,   4995,   8704,  15792,   1766,   2848,   5049,   9155,  15687,  23760,
   2617,   4600,   8856,  17297,  31054,  44370,   3969,   7087,  14260,  29920,  58596,  88759,
   6050,   9581,  18287,  39228,  84426, 149411,   1885,   2265,   3281,   5345,   9218,  16483,
   2228,   3239,   5438,   9850,  17718,  28730,   3082,   5119,   9667,  19507,  38146,  61706,
   4607,   8186,  16740,  37316,  81980, 146739,   7031,  11987,  24418,  57373, 142858, 313654,
    602,    881,   1550,   3309,   9232,  33596,    825,   1259,   2121,   3794,   7576,  19310,
   1327,   1886,   3096,   5245,   8912,  16624,   2673,   2944,   4332,   7053,  11411,  18099,
   7588,   5553,   6025,   8343,  12780,  19757,    746,   1118,   1876,   3419,   7126,  18984,
   1051,   1705,   2945,   5104,   8784,  16540,   1597,   2611,   4611,   8080,  13147,  19720,
   2634,   3828,   6558,  11666,  19339,  27148,   5357,   5699,   8049,  13326,  22532,  33665,
   1060,   1418,   2248,   3851,   7062,  15073,   1356,   2138,   3690,   6461,  10850,  17685,
   1969,   3321,   6094,  11189,  18843,  26749,   3009,   4925,   9160,  17599,  31318,  44459,
   5080,   6791,  11291,  21276,  40147,  63594,   2021,   1936,   2494,   3817,   6533,  12943,
   1903,   2441,   3772,   6386,  10907,  18143,   2334,   3538,   6176,  11453,  20518,  31753,
   3288,   5295,   9832,  19661,  38235,  61593,   5168,   7624,  13526,  27441,  57907, 107164,
    629,    917,   1527,   2856,   6357,  18262,    872,   1373,   2339,   4101,   7427,  15354,
   1338,   2110,   3651,   6404,  10775,  17608,   2315,   3213,   5331,   9383,  15844,  23778,
   5090,   5214,   7073,  11397,  19282,  30118,    805,   1240,   2091,   3693,   6903,  14949,
   1179,   1981,   3527,   6276,  10648,  17519,   1818,   3164,   5909,  10960,  18573,  26513,
   2872,   4766,   8957,  17329,  30975,  44137,   4980,   6668,  11125,  21040,  39826,  63266,
   1099,   1633,   2713,   4743,   8403,  15491,   1570,   2633,   4792,   8822,  15260,  23311,
   2408,   4348,   8524,  16834,  30415,  43656,   3739,   6786,  13833,  29282,  57650,  87621,
   5832,   9280,  17838,  38514,  83287, 147897,   1732,   2104,   3095,   5111,   8917,  16153,
   2068,   3053,   5200,   9524,  17268,  28206,   2899,   4885,   9341,  19026,  37432,  60820,
   4390,   7885,  16291,  36602,  80841, 145225,   6805,  11657,  23894,  56487, 141344, 311545,
    736,    896,   1323,   2243,   4528,  11925,    876,   1255,   2019,   3473,   6269,  12680,
   1232,   1893,   3236,   5792,  10216,  17448,   1979,   2942,   5055,   9324,  16973,  27786,
   3965,   4858,   7361,  12952,  24196,  42308,    843,   1184,   1874,   3215,   5905,  12368,
   1153,   1821,   3161,   5708,  10127,  17380,   1723,   2936,   5498,  10619,  19475,  30659,
   2723,   4667,   9052,  18618,  36819,  59991,   4691,   7049,  12764,  26347,  56305, 105197,
   1104,   1608,   2625,   4606,   8341,  15572,   1573,   2584,   4689,   8911,  16506,  27378,
   2435,   4380,   8728,  18219,  36337,  59546,   3913,   7309,  15528,  35507,  79239, 143258,
   6357,  11077,  23066,  55213, 139377, 309088,   1645,   2196,   3385,   5794,  10567,  19814,
   2173,   3362,   5902,  11337,  22284,  40307,   3248,   5654,  11217,  24435,  53859, 102525,
   5220,   9849,  21562,  53212, 136705, 306182,   8611,  16224,  36621,  98644, 302695, 920455
};


/*/////////////////////////////////////////////////
	CALCHISTORYINDEX

	this will calculate the appropiate history
	index for the current collection count
	the history index is used to reference the
	expected payout table above

	This routine should only be called if and
	only if the collection count is incomplete.

	calling this routine with a non-incomplete
	collection count will yield incorrect results
/////////////////////////////////////////////////*/
int HikeSlotGuts::CalcHistoryIndex()
{
	//some simple cheap and easy factorials
	static factorial[8] =
	{
		0, 1, 2, 6, 24, 120, 720, 5040
	};

	//the lookup index being calculated
	int historyIndex=0;

	//look at the collection count for each dieface
	for (int i=1; i<=m_maximumDieface; i++)
	{
		//do the magical counting method math ... oooo factorials!
		historyIndex += m_nCollected[i]*(m_maximumHistory/factorial[i]);
	}

	//and the answer is
	return historyIndex;
}


/*////////////////////////////////
	DOZEROCOLLECTIONCOUNTS
	zero the internal game state
////////////////////////////////*/
void
HikeSlotGuts::DoZeroCollectionCounts()
{
	//set the internal state to zero
	for (int i=0; i<= m_maximumDieface; i++)
		m_nCollected[i]=0;
}


/*//////////////////////////////////////////////
	DOINITCOLLECTIONCOUNTS
	set the server internal gamestate to zero
	and then count each dieface
//////////////////////////////////////////////*/
void
HikeSlotGuts::DoLoadCollectionCounts()
{
	//zero the collection counts
	DoZeroCollectionCounts();

	//get the number of diefaces collected
	int nCollected = GetNDiefacesCollected();

	//sanity check
	if (!IsPaylineValid(nCollected+1))
		assert(0);

	//restore the collection state
	for (int i=1; i<=nCollected; i++)
	{
		//read the database value
		int dieface = GetPaylineDieface(i);

		//sanity check
		if (!IsDiefaceValid(dieface))
			assert(0);

		//add it to the collection
		m_nCollected[ dieface ]++;
	}
}


/*//////////////////////////////////////////////
	RESETGAMESTATE
	set the servers internal gamestate to zero
//////////////////////////////////////////////*/
void
HikeSlotGuts::DoResetGameState()
{
	//set the collection count to 0
	SetNDiefacesCollected(0);

	//set the spin history to 0
	for (int i=SGIDX_SPIN_1DIEFACE; i<=SGIDX_SPIN16DIEFACE; i++)
		SetPaylineDieface( i,0 );

	//set the gamestate
	SetGameState( HikeGameState__RESET );

	//zero the bet
	SetNCreditsBet( 0 );

	//zero paid amount
	SetNPayoutCentiCredits( 0 );

	//zero expected payout
	SetNBuyoutCentiCredits( 0 );

	//setup the collection counts
	DoLoadCollectionCounts();
}


/*//////////////////////////
	HIKESLOTGUTS
	The constructor
	1) zero the collection
	2) init the game state
//////////////////////////*/
HikeSlotGuts::HikeSlotGuts()
{
	//zero the collection counts
	DoZeroCollectionCounts();
}


/*/////////////////////
	~HIKESLOTGUTS
	The destructor
/////////////////////*/
HikeSlotGuts::~HikeSlotGuts()
{
	//say goodbye gracie
}


/*////////////////////////////////////////////////////////
	ROLLDIE

	Roll a single normalized die with N number of faces

	The most obvious way to roll a die is to simply grab
	a random number and do a modulo N on it where N is
	the number of diefaces.  This is cheap and easy but
	not the most accurate way.  Here's why.

	For the sake of simplicity let's say the random
	number generator returns 4-bits (values 0-15) and
	we want to roll a 6-sided die.  Using the simple
	modulo method above,
	R values  0- 5 will map to diefaces 1-6
	R values  6-11 will map to diefaces 1-6
	R values 12-15 will map to diefaces 1-4
	and this is a problem.  If each R value is evenly
	distributed then our 6-sided die will be biased
	against the 5 and 6 because the random number
	generator does not have R values 16 and 17 because
	it would take a 5th bit to hold these values and it
	only has 4-bits.  The resulting bias is significant
	in that the 1-4 each have a 3/16 chance of appearing
	but the 5 and 6 only have a 2/16 chance of appearing

	If we use a random number generator with 5 bits we
	still have the same problem.  There are 32 possible
	values with 5 bits and the first 30 will map evenly
	to the 6-sided die but the last 2 will not so the die
	will be weighted toward the 1 and 2.

	The simplest way to solve this problem for any random
	number generator bitsize and any die size is to just
	disallow the few values that bias the die. So 
	in the 4-bit case we ignore R values 12-15 and
	in the 5-bit case we ignore R values 30-31.
////////////////////////////////////////////////////////*/

randtype HikeSlotGuts::RollDie(unsigned nFaces)
{
	//need a value for the maximum random number possible
	const randtype maxbits = (~0);

	//calculate the maximum acceptable value from the random number generator
	const randtype maxrand = maxbits - (((maxbits%nFaces)+1)%nFaces);

	//get an acceptable random number
	for (randtype r=getrand(); r>maxrand; r=getrand());

	//map (0,nFaces-1) to (1,nFaces)
	r=(r%nFaces)+1;

	//??	always finish game on first spin
	//r=1;

	//all done
	return r;
}

/*/////////////////////////////////////
	CALCNDIEFACESCOLLECTED

	return the total number diefaces
	that have been collected for this
	game

/*/////////////////////////////////////
int
HikeSlotGuts::CalcNDiefacesCollected()
{
	//nothing collected by default
	int nDiefacesCollected=0;

	//for each possible dieface
	for (int i=1; i<=m_maximumDieface; i++ )
		//sum the counts
		nDiefacesCollected += m_nCollected[i];

	//the total
	return nDiefacesCollected;
}

/*/////////////////////////////////////
	DOCOLLECTDIEFACE

	add the dieface to the collection
	called from HandleNextRoll

/////////////////////////////////////*/
void
HikeSlotGuts::DoCollectDieface( randtype dieface )
{
	//ASSERT collection must be incomplete
	if (!IsCollectionIncomplete())
		assert(0);

	//ASSERT dieface must be ok
	if (!IsDiefaceValid(dieface))
		assert(0);

	//increment the count for this dieface
	m_nCollected[ dieface ]++;

	//how many diefaces have been collected
	int nDiefacesCollected = CalcNDiefacesCollected();

	//update the number of diefaces collected for this game
	SetNDiefacesCollected( nDiefacesCollected );

	//record the dieface we just collected
	SetPaylineDieface( nDiefacesCollected, dieface );

	//the collection is incomplete
	if (m_nCollected[ dieface ] < dieface)
		DoCollectionIncomplete();

	else

	//the collection is complete
	if (m_nCollected[ dieface ] == dieface)
		DoCollectionComplete();

	else

	//we have a very serious problem with the universe
		assert(0);
}


/*

+--ALL-COLLECTIONS--------------+
|								|
|	INVLALID COLLECTIONS		|
|								|
|	+---VALID-COLLECTIONS---+	|
|	|						|	|
|	|	COMPLETE			|	|
|	|						|	|
|	|	+---INCOMPLETE--+	|	|
|	|	|				|	|	|
|	|	|	INPROGRESS	|	|	|
|	|	|				|	|	|
|	|	|	+-----+		|	|	|
|	|	|	|EMPTY|		|	|	|
|	|	|	+-----+		|	|	|
|	|	+---------------+	|	|
|	+-----------------------+	|
+-------------------------------+

HIKE COLLECTION STATES:

EMPTY		:	all collection counters are zero

INPROGRESS	:	INCOMPLETE and not EMPTY

INCOMPLETE	:	all collection counters are zero or more
			:	no collection counter is full

COMPLETE	:	all collection counters are zero or more
			:	exactly one and only one collection counter is full

VALID		:	COMPLETE or INCOMPLETE

*/


/*///////////////////////////////////////////////
	ISCOLLECTIONEMPTY

	The collection is empty if and only if
	the history count for all diefaces is 0

///////////////////////////////////////////////*/
int HikeSlotGuts::IsCollectionEmpty()
{
	//for each possible die face
	for (int i=1; i<=HikeSlotGuts::m_maximumDieface; i++)

	//the count must be zero
	if (m_nCollected[i] != 0)

		//not empty
		return (0);

	//empty
	return (1);
}


/*///////////////////////////////////////////////////////
	ISCOLLECTIONINCOMPLETE

	The collection is complete if and only if:

	1) the history counts for all diefaces >= 0
	2) the history counts for all diefaces < the dieface
	3) the number of winning diefaces == 0

///////////////////////////////////////////////////////*/
int HikeSlotGuts::IsCollectionIncomplete()
{
	//for each possible die face
	for (int i=1; i<=HikeSlotGuts::m_maximumDieface; i++)
	{
		//cannot collect a negative number of dice
		if (m_nCollected[i] < 0)
			return 0;
		//cannot have any winners
		if (m_nCollected[i] >= i)
			return 0;
	}

	//valid
	return(1);
}


/*///////////////////////////////////////////////////////
	ISCOLLECTIONCOMPLETE

	The collection is complete if and only if

	1) the history counts for all diefaces >= 0
	2) the history counts for all diefaces <= the dieface
	3) the number of winning diefaces == 1

///////////////////////////////////////////////////////*/
int HikeSlotGuts::IsCollectionComplete()
{
	//number of winners
	int nWinners=0;

	//for each possible dieface
	for (int i=1; i<=HikeSlotGuts::m_maximumDieface; i++)
	{
		//cannot be negative dice thrown
		if (m_nCollected[i] < 0)
			return 0;
		//cannot throw a die more than die times
		if (m_nCollected[i] > i)
			return 0;
		//count the winners
		if (m_nCollected[i] == i)
			nWinners++;
	}
	//must have exactly 1 winner
	if (nWinners != 1)
		return 0;

	//valid
	return(1);
}


/*/////////////////////////////////////////////////////////
	ISCOLLECTIONVALID

	The collection is valid if and only if:

	1) the history counts for all diefaces >= 0
	2) the history counts for all diefaces <= the dieface
	3) the number of winning diefaces <= 1

/////////////////////////////////////////////////////////*/
int HikeSlotGuts::IsCollectionValid()
{
	//number of winners
	int nWinners=0;

	//for each possible dieface
	for (int i=1; i<=HikeSlotGuts::m_maximumDieface; i++)
	{
		//cannot be negative dice thrown
		if (m_nCollected[i] < 0)
			return 0;
		//cannot throw a die more than die times
		if (m_nCollected[i] > i)
			return 0;
		//count the winners
		if (m_nCollected[i] == i)
			nWinners++;
	}
	//cannot have more than 1 winner
	if (nWinners > 1)
		return 0;

	//valid
	return(1);
}


/*/////////////////////////////////////////////////////////
	ISCOLLECTIONINPROGRESS

	The collection is in progress if and only if:

	1) the collection is incomplete
	2) the collection is not empty

/////////////////////////////////////////////////////////*/
int HikeSlotGuts::IsCollectionInProgress()
{
	//game over means not in progress
	if (GetGameState() == HikeGameState__GAMEOVER)
		return 0;

	//must be incomplete
	if (IsCollectionIncomplete())

	//must not be empty
	if (!IsCollectionEmpty())

		//in progress
		return 1;

	//not in progress
	return 0;
}

/*/////////////////////////////////////////////////////////
	ISPAYLINEVALID
	sanity check for payline
/////////////////////////////////////////////////////////*/
int HikeSlotGuts::IsPaylineValid( int payline )
{
	//must be 1 or more
	if (payline < 1)
		return 0;

	//must be max or less
	if (payline > m_maximumPayline)
		return 0;

	//valid
	return 1;
}

/*/////////////////////////////////////////////////////////
	ISDIEFACEVALID
	sanity check for dieface
/////////////////////////////////////////////////////////*/
int HikeSlotGuts::IsDiefaceValid( int dieface )
{
	//must be 1 or more
	if (dieface < 1)
		return 0;

	//must be max or less
	if (dieface > m_maximumDieface)
		return 0;

	//valid
	return 1;
}

/*/////////////////////////////////////////////////////////
	ISHISTORYVALID
	sanity check for collection index
/////////////////////////////////////////////////////////*/
int HikeSlotGuts::IsHistoryValid( int history )
{
	//must be 0 or more
	if (history < 0)
		return 0;

	//must max or less
	if (history > m_maximumHistory)
		return 0;

	//valid
	return 1;
}

/*//////////////////////////////////
	ISPAYOUTVALID
	sanity check for payout amount
//////////////////////////////////*/
int HikeSlotGuts::IsPayoutValid(int payout)
{
	//payout must be 1 centicredit or more
	if (payout < 1)
		return 0;

	//anything more than this is a red flag
	#define TENTHOUSANDCREDITS (1000000)
	if (payout > TENTHOUSANDCREDITS)
		return 0;

	//valid
	return 1;
}

/*/////////////////////////////////////////////////////////
	CALCPAYOUT
	the one and only routine that reads the PayTable
/////////////////////////////////////////////////////////*/
int HikeSlotGuts::CalcPayout( int dieface, int payline )
{
	//collection must be complete
	if (!IsCollectionComplete())
		return 0;

	//dieface must be valid
	if (!IsDiefaceValid(dieface))
		return 0;

	//payline must be valid
	if (!IsPaylineValid(payline))
		return 0;

	//lookup the payout amount in milli-credits for this event
	int payoutMilliCredits = m_PayTable6Stop[payline][dieface];

	//get the number of credits bet
	int nCreditsBet = GetNCreditsBet();

	//scale to the number of credits bet
	payoutMilliCredits *= nCreditsBet;

	//round down to centi-credits
	int payoutCentiCredits = payoutMilliCredits / 10;

	//sanity check on the payout
	if (!IsPayoutValid(payoutCentiCredits))
		assert(0);

	//ok
	return payoutCentiCredits;
}

/*/////////////////////////////////////////////////////////
	CALCEXPECTEDPAYOUT
	the one and only routine that reads the ExPayTable
	this routine does not apply the discount logic
	see CalcBuyoutOffer for the discount strategy
/////////////////////////////////////////////////////////*/
int HikeSlotGuts::CalcExpectedPayout( int history )
{
	//collection must be complete
	if (!IsCollectionIncomplete())
		return 0;

	//collection index must be valid
	if (!IsHistoryValid(history))
		return 0;

	//lookup the present expected payout
	int expectedPayoutMilliCredits = m_ExpectedPayout6Stop[history];

	//get the number of credits bet on this game
	int nCreditsBet = GetNCreditsBet();

	//scale expected payout to the number of credits bet
	expectedPayoutMilliCredits *= nCreditsBet;

	//round down to centi-credits
	int expectedPayoutCentiCredits = expectedPayoutMilliCredits / 10;

	//sanity check on the expected payout
	if (!IsPayoutValid(expectedPayoutCentiCredits))
		assert(0);

	//ok
	return expectedPayoutCentiCredits;
}

/*//////////////////////////////////////////////
	CALCBUYOUT

	this routine is the heart and soul of Hike
	this is where we calculate a price for the
	players game that is actually less than the
	expected value of the game.

	this routine should only be called from
	HandleCollectionIncomplete which is the only
	valid point at which to offer to buy the
	players game

//////////////////////////////////////////////*/
int HikeSlotGuts::CalcBuyout()
{
	//it would be better to put these in the database
	static double m_percentageBuyout[17] =
	{
	//	 0     1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16
	//-------------------------------------------------------------------------------------
		0.0, .99, .99, .98, .98, .97, .97, .96, .96, .95, .95, .94, .94, .93, .93, 1.0, 0.0
	};

	//need the history index for 
	//the current collection count
	int historyIndex = CalcHistoryIndex();

	//now lookup the undiscounted expected payout in centi credits
	int nBuyoutCentiCredits = CalcExpectedPayout(historyIndex);

	/*
	//what is the next payline
	int nextPayline = GetNDiefacesCollected();

	//start at 100% and work our way down to 95%
	double percentageBuyout = (100.0 - nextPayline/3)/100.0;

	//but jump back to 100% for last payline
	//so we don't look like we're lowballing even though we are
	if (nextPayline==m_maximumPayline)
		percentageBuyout=1.0;
	*/

	//get the number of dice collected so far
	int nDice = GetNDiefacesCollected();

	//lookup the buyout percentage
	double percentageBuyout = m_percentageBuyout[ nDice ];

	//apply the discount
	nBuyoutCentiCredits *= percentageBuyout;

	//and the offer is
	return nBuyoutCentiCredits;
}

/*//////////////////////////////////////////
	DOCOLLECTIONINCOMPLETE

	called after each spin
	that does not end the game
//////////////////////////////////////////*/
void
HikeSlotGuts::DoCollectionIncomplete()
{
	//the collection must be incomplete
	if (!IsCollectionIncomplete())
		assert(0);

	//calculate the current buyout offer for the game
	int nBuyoutCentiCredits = CalcBuyout();

	//remember the expected value for the game
	SetNBuyoutCentiCredits(nBuyoutCentiCredits);
}

/*//////////////////////////////////////////
	DOPAYOUT

	called from either 
		DoCollectionComplete or
		HandleSellGame

//////////////////////////////////////////*/
void HikeSlotGuts::DoPayout( int nPayoutCentiCredits )
{
	//record the number of credits paid
	SetNPayoutCentiCredits( nPayoutCentiCredits );

	//gameover
	SetGameState( HikeGameState__GAMEOVER );
}

/*//////////////////////////////////////////
	DOCOLLECTIONCOMPLETE

	called once
	after the spin that ends the game
//////////////////////////////////////////*/
void
HikeSlotGuts::DoCollectionComplete()
{
	//the collection must be complete
	if (!IsCollectionComplete())
		assert(0);

	//get the winning event
	int payline = GetNDiefacesCollected();
	int dieface = GetPaylineDieface(payline);

	//get the payout in centi credits
	int payoutCentiCredits = CalcPayout( dieface, payline );

	//show me the money
	DoPayout( payoutCentiCredits );
}

/*//////////////////////////////////////////
	ISNEXTGAMEALLOWED

//////////////////////////////////////////*/
int
HikeSlotGuts::IsNextGameAllowed()
{
	//get the gamestate
	HikeGameState gamestate = GetGameState();

	//cannot proceed to next game while in progress
	if (gamestate == HikeGameState__WAIT_SPINORSELL)
		return 0;

	//allowed
	return 1;
}

/*//////////////////////////////////////////
	HANDLENEXTGAME

	called in response to the player 
	request for another game
//////////////////////////////////////////*/
void
HikeSlotGuts::HandleNextGame( int nCreditsBet )
{
	//is the next game allowed
	if (!IsNextGameAllowed())
		return;

	//goto known start state
	DoResetGameState();

	//sanity check
	if (GetGameState() != HikeGameState__RESET)
		assert(0);

	//need a bet now
	SetGameState( HikeGameState__WAIT_NEXTGAME );

	//sanity check
	if (GetGameState() != HikeGameState__WAIT_NEXTGAME )
		assert(0);

	//record the bet
	SetNCreditsBet( nCreditsBet );

	//sanity check
	if (GetNCreditsBet() != nCreditsBet)
		assert(0);

	//waiting for user response
	SetGameState( HikeGameState__WAIT_SPINORSELL );

	//sanity check
	if (GetGameState() != HikeGameState__WAIT_SPINORSELL )
		assert(0);

	//rock on!
}

/*//////////////////////////////////////////
	ISFREEGAMEALLOWED

//////////////////////////////////////////*/
int
HikeSlotGuts::IsFreeGameAllowed()
{
	if (!IsNextGameAllowed())
		return 0;

	//allowed
	return 1;
}

/*//////////////////////////////////////////
	HANDLEFREEGAME

	called in response to the player 
	request for a free game
//////////////////////////////////////////*/
void
HikeSlotGuts::HandleFreeGame( int nCreditsBet )
{
	//is this allowed
	if (!IsFreeGameAllowed())
		return;

	//see if there are enough unpaid centi credits to pay for the game
	int nUnpaidMilliCredits = GetNUnpaidMilliCredits();

	//convert to unpaid credits
	int nUnpaidCredits = nUnpaidMilliCredits / 1000;

	//does the player have enough
	if (nUnpaidCredits < nCreditsBet)
		return;

	//deduct the free bet	
	nUnpaidMilliCredits -= (nCreditsBet*1000);

	//remember the reduced amount
	SetNUnpaidMilliCredits( nUnpaidMilliCredits );

	//remember how many credits are riding on this game
	SetNCreditsBet( nCreditsBet );
}

/*//////////////////////////////////////////
	ISNEXTSPINALLOWED

//////////////////////////////////////////*/
int
HikeSlotGuts::IsNextRollAllowed()
{
	//get the game state
	HikeGameState gamestate = GetGameState();

	//must be waiting for spin or sell
	if (gamestate != HikeGameState__WAIT_SPINORSELL)
		return 0;

	//allowed
	return 1;
}

/*//////////////////////////////////////////
	HANDLENEXTROLL

	called in response to the player 
	request for another dieroll
//////////////////////////////////////////*/
void
HikeSlotGuts::HandleNextRoll()
{
	//is this allowed
	if (!IsNextRollAllowed())
		return;

	//must have an incomplete collection
	if (!IsCollectionIncomplete())
		assert(0);

	//roll a single m_maximumDieface sided die
	randtype dieface = RollDie( m_maximumDieface );

	//ok now add this dieface to the collection
	DoCollectDieface(dieface);
}

/*//////////////////////////////////////////
	ISSELLGAMEALLOWED

//////////////////////////////////////////*/
int
HikeSlotGuts::IsSellGameAllowed()
{
	//get the game state
	HikeGameState gamestate = GetGameState();

	//must be waiting for spin or sell
	if (gamestate != HikeGameState__WAIT_SPINORSELL)
		return 0;

	//cannot sell for less than 1 credit
	int nBuyoutCentiCredits = GetNBuyoutCentiCredits();
	if (nBuyoutCentiCredits < 1)
		return 0;

	//allowed
	return 1;
}


/*//////////////////////////////////////////
	HANDLESELLGAME

	called in response to the player 
	request to sell the game
//////////////////////////////////////////*/
void
HikeSlotGuts::HandleSellGame()
{
	//is this allowed
	if (!IsSellGameAllowed())
		return;

	//get the amount offered from the subgame data
	int nBuyoutCentiCredits = GetNBuyoutCentiCredits();

	//show me the money
	DoPayout( nBuyoutCentiCredits );

	//buy the game from the player
	SetGameState( HikeGameState__SELLGAME );
}

/*////////////////////////////////////////////////
	ISNCREDITSBETVALID
	Handle bet amounts from client
	This should get called exactly once per game
////////////////////////////////////////////////*/
int
HikeSlotGuts::IsNCreditsBetValid(int nCreditsBet)
{
	//cannot have a negative bet
	if (nCreditsBet < 1)
		return 0;

	//cannot have a bet more than the max credits
	if (nCreditsBet > m_maximumCredits)
		return 0;

	//bet is ok
	return 1;
}

/*////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
	BEGIN SLOTGUTS VIRTUAL METHOD HIJACKING
//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////*/


/*/////////////////////////////////////////////////////////
	PLAYBONUSGAME
	Handle 'B'onus game request from client
	The player is requesting to play the hike bonus game
	The hike bonus game is simply the buyout option
/////////////////////////////////////////////////////////*/
void 
HikeSlotGuts::playBonusGame(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData)
{
	HandleSellGame();

	/*
	//get the message from the player
	HikeMessage message = (HikeMessage)bonusGameType;

	//call the appropiate handler
	switch (message)
	{
	case HikeFreeGame	:	HandleFreeGame( bonusData[0] ); break;
	case HikeSellGame	:	HandleSellGame(); break;
	}
	*/
}

/*/////////////////////
	ISVALIDGUTS

/////////////////////*/
bool8 
HikeSlotGuts::isValidGuts()
{
    if (getGutsId() == HikeSlotGuts::m_theGutsID)
		return true;
	return false;
}

/*/////////////////////////////////
	ISVALIDBET
	called by slotguts every time	
/////////////////////////////////*/
bool8
HikeSlotGuts::isValidBet(uint8 bet)
{
	//zero bet is valid if and only if
	//the game is in progress
	if (bet == 0)
	if (IsCollectionInProgress())
		return 1;

	//non-zero bets are valid if and only if
	//the game is not in progress
	if (bet  > 0)
	if (!IsCollectionInProgress())
		return 1;

	//not valid by default
	return 0;
}


/*/////////////////////////////////////////////////////
	ISGAMECOMPLETE
	called by guts to determine if it is time to 
	close the game in the database
/////////////////////////////////////////////////////*/
/*
bool8
HikeSlotGuts::isGameComplete()
{
	//get the game state
	HikeGameState gamestate = GetGameState();

	//check for end of game
	if (gamestate == HikeGameState__GAMEOVER)

		//yes game is over
		return 1;

	//no game is not over
	return 0;
}
*/

/*/////////////////////////////////////////////////////
	SPIN
	called by slotguts every time client sends an 'S'
/////////////////////////////////////////////////////*/
SlotSpin*
HikeSlotGuts::spin
(
	uint8 bet,			//the players bet in credits
	int32 mbbet,		//multibank bet in cents
	uint8 *lines,		//the array of bet lines
	uint8 lineCount,	//the number of lines bet
	uint8 bonusGame		//the bonus game id
)
{
	if (GetGameState() == HikeGameState__SELLGAME)
		SetGameState( HikeGameState__GAMEOVER );

	//need a SlotSpin pointer
	SlotSpin *pSpin = NULL;

	//get the pointer if it is there
	if (m_spins.size() > 0)
		pSpin = m_spins.back();

	//it might be time to begin a new game
	if (pSpin == NULL)
	{
		//tell hike that a new game is starting
		HandleNextGame( bet );

		//GUTS MAGIC/////////////////////////////
		pSpin = new SlotSpin();
		for ( uint8 i = 0; i < lineCount; i++ )
			pSpin->addLine(lines[i]);
		pSpin->setBet(bet);
		pSpin->setMultibankBet(mbbet);
		pSpin->setBonusGameType(bonusGame);

		//save the spin
		m_spins.push_back(pSpin);
		/////////////////////////////////////////
	}

	if (m_spins.size() != 1)
		assert(0);

	if (pSpin == NULL)
		assert(0);

	//next die roll please
	if (IsNextRollAllowed())
		HandleNextRoll();

	//take a peek at the game state
	HikeGameState gamestate = GetGameState();

	//is the game over
	if (gamestate == HikeGameState__GAMEOVER)
	{
		//set all the stops in the spin record/////////////////////
		int k=0;
		int nDiefacesCollected = GetNDiefacesCollected();
		uint8 diefaceList[32];
		for (int i=SGIDX_SPIN_1DIEFACE; i<=nDiefacesCollected; i++)
			diefaceList[k++] = GetPaylineDieface(i);
		pSpin->setStops( (uint8)nDiefacesCollected, diefaceList );
		///////////////////////////////////////////////////////////

		//get the number of centi credits in the payout
		int nPayoutCentiCredits	= GetNPayoutCentiCredits();

		//convert to server cents
		int nPayoutCents	= (nPayoutCentiCredits * getInfo()->creditValue) / 100;

		//set the payout
		pSpin->setPayoutAmount( 1, nPayoutCents, 0,0 );

		//the spin is complete
		pSpin->setSpinComplete(true);

		//all done
		setGameComplete(true);
	}

	//the SlotSpin pointer
	return pSpin;
}


/*////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
	END SLOTGUTS VIRTUAL METHOD HIJACKING
//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////*/
